#define _POSIX_C_SOURCE 199309L
#include "monitor.h"
#include "client.h"
#include "util.h"
#include <time.h>
#include <string.h>

#include <wlr/interfaces/wlr_output.h>

struct wl_list clients; /* tiling order */

static void rendermon(struct wl_listener *listener, void *data);
static void cleanupmon(struct wl_listener *listener, void *data);

Monitor *monitor_create(Server *server, struct wlr_output *wlr_output)
{
    Monitor *m = ecalloc(1, sizeof(*m));
    m->server = server;
    m->wlr_output = wlr_output;

    wlr_output_init_render(wlr_output, server->alloc, server->drw);

    /* Initialize monitor state using configured rules */
    for (size_t i = 0; i < LENGTH(m->layers); i++)
        wl_list_init(&m->layers[i]);
    m->tagset[0] = m->tagset[1] = 1;
    const MonitorRule *r;
    for (r = monrules; r < END(monrules); r++) {
        if (!r->name || strstr(wlr_output->name, r->name)) {
            m->mfact = r->mfact;
            m->nmaster = r->nmaster;
            wlr_output_set_scale(wlr_output, r->scale);
            wlr_xcursor_manager_load(server->cursor->cursor_mgr, r->scale);
            m->lt[0] = m->lt[1] = r->lt;
            wlr_output_set_transform(wlr_output, r->rr);
            break;
        }
    }

    /* The mode is a tuple of (width, height, refresh rate), and each
     * monitor supports only a specific set of modes. We just pick the
     * monitor's preferred mode; a more sophisticated compositor would let
     * the user configure it. */
    wlr_output_set_mode(wlr_output, wlr_output_preferred_mode(wlr_output));
    wlr_output_enable_adaptive_sync(wlr_output, 1);
    wlr_output_update_custom_mode(wlr_output, 1920, 1080, 0); /*added by biwenjie*/

    /* Set up event listeners */
    LISTEN(&wlr_output->events.frame, &m->frame, rendermon);
    LISTEN(&wlr_output->events.destroy, &m->destroy, cleanupmon);

    wlr_output_enable(wlr_output, 1);
    if (!wlr_output_commit(wlr_output)) {
        cleanup_monitor(m);
        return NULL;
    }

    wl_list_insert(&server->mons, &m->link);
//    printstatus();

    /* Adds this to the output layout in the order it was configured in.
     *
     * The output layout utility automatically adds a wl_output global to the
     * display, which Wayland clients can see to find out information about the
     * output (such as DPI, scale factor, manufacturer, etc).
     */
    m->scene_output = wlr_scene_output_create(server->scene, wlr_output);
    wlr_output_layout_add_auto(server->output_layout, wlr_output);

    /* If length == 1 we need update selmon.
     * Maybe it will change in run(). */
    if (wl_list_length(&server->mons) == 1) {
        Client *c;
        server->selmon = m;
        /* If there is any client, set c->mon to this monitor */
        wl_list_for_each(c, &clients, link)
            setmon(c, m, c->tags);
    }
    return m;
}

void tile(Monitor *m)
{
    unsigned int i, n = 0, h, mw, my, ty;
    Client *c;

    wl_list_for_each(c, &clients, link)
        if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen)
            n++;
    if (n == 0)
        return;

    if (n > m->nmaster)
        mw = m->nmaster ? m->w.width * m->mfact : 0;
    else
        mw = m->w.width;
    i = my = ty = 0;
    wl_list_for_each(c, &clients, link) {
        if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
            continue;
        if (i < m->nmaster) {
            h = (m->w.height - my) / (MIN(n, m->nmaster) - i);
            resize(c, m->w.x, m->w.y + my, mw, h, 0);
            my += c->geom.height;
        } else {
            h = (m->w.height - ty) / (n - i);
            resize(c, m->w.x + mw, m->w.y + ty, m->w.width - mw, h, 0);
            ty += c->geom.height;
        }
        i++;
    }
}

void
monocle(Monitor *m)
{
    Client *c;

    wl_list_for_each(c, &clients, link) {
        if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
            continue;
        resize(c, m->w.x, m->w.y, m->w.width, m->w.height, 0);
    }
    focusclient(focustop(m), 1);
}


void rendermon(struct wl_listener *listener, void *data)
{
    Monitor *m = wl_container_of(listener, m, frame);
    render_monitor(m);
}


void
cleanupmon(struct wl_listener *listener, void *data)
{
    struct wlr_output *wlr_output = data;
    Monitor *m = wlr_output->data;
    cleanup_monitor(m);
}

void render_monitor(Monitor *m)
{
    /* This function is called every time an output is ready to display a frame,
     * generally at the output's refresh rate (e.g. 60Hz). */
    Client *c;
    int skip = 0;
    struct timespec now;

    clock_gettime(CLOCK_MONOTONIC, &now);

    /* Render if no XDG clients have an outstanding resize and are visible on
     * this monitor. */
    /* Checking m->un_map for every client is not optimal but works */
    wl_list_for_each(c, &clients, link) {
        if ((c->resize && m->un_map) || (c->type == XDGShell
                && (c->surface.xdg->pending.geometry.width !=
                c->surface.xdg->current.geometry.width
                || c->surface.xdg->pending.geometry.height !=
                c->surface.xdg->current.geometry.height))) {
            /* Lie */
            wlr_surface_send_frame_done(client_surface(c), &now);
            skip = 1;
        }
    }
    if (!skip && !wlr_scene_output_commit(m->scene_output))
        return;
    /* Let clients know a frame has been rendered */
    wlr_scene_output_send_frame_done(m->scene_output, &now);
    m->un_map = 0;
}

void cleanup_monitor(Monitor *m)
{
    int nmons, i = 0;

    wl_list_remove(&m->destroy.link);
    wl_list_remove(&m->frame.link);
    wl_list_remove(&m->link);
    wlr_output_layout_remove(m->server->output_layout, m->wlr_output);
    wlr_scene_output_destroy(m->scene_output);

    if ((nmons = wl_list_length(&m->server->mons)))
        do /* don't switch to disabled mons */
            m->server->selmon = wl_container_of(m->server->mons.prev, m->server->selmon, link);
        while (!m->server->selmon->wlr_output->enabled && i++ < nmons);

    focusclient(focustop(m->server->selmon), 1);
    closemon(m);
    free(m);
}

void closemon(Monitor *m)
{
    /* move closed monitor's clients to the focused one */
    Client *c;

    wl_list_for_each(c, &clients, link) {
        if (c->isfloating && c->geom.x > m->m.width)
            resize(c, c->geom.x - m->w.width, c->geom.y,
                c->geom.width, c->geom.height, 0);
        if (c->mon == m)
            setmon(c, m->server->selmon, c->tags);
    }
//	printstatus();
}

void arrange(Monitor *m)
{
    struct input_popup *popup;
    Client *c;
    wl_list_for_each(c, &clients, link) {
        wlr_scene_node_set_enabled(c->scene, VISIBLEON(c, c->mon));
        wl_list_for_each(popup, &c->input_popups, client_link) {
            wlr_scene_node_set_enabled(popup->scene, VISIBLEON(c, c->mon));
        }
    }

    if (m->lt[m->sellt]->arrange)
        m->lt[m->sellt]->arrange(m);
    /* TODO recheck pointer focus here... or in resize()? */
}

Monitor *xytomon(double x, double y, struct wlr_output_layout *output_layout)
{
    struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y);
    return o ? o->data : NULL;
}
